import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart' hide Response;
import 'package:path_provider/path_provider.dart';
import 'package:s_utils/s_utils.dart';
import 'package:sentry_dio/sentry_dio.dart';
import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';

import 'auth_interceptor.dart';

final globalCacheOptions = CacheOptions(
  store: MemCacheStore(),
  policy: CachePolicy.request,
  hitCacheOnErrorExcept: [401, 403],
  maxStale: const Duration(days: 7),
  priority: CachePriority.low,
  cipher: null,
  keyBuilder: CacheOptions.defaultCacheKeyBuilder,
  allowPostMethod: false,
);

class SHttp {
  static SHttp? _instance;
  static CacheOptions cacheOptions = globalCacheOptions;
  static bool addSentry = true;
  static bool _showApiLog = true;
  static String headerTokenKey = "Authorization";
  static String headerToken = "";

  static Dio _dio = Dio();
  static Dio _tokenDio = Dio();
  static Dio _retryDio = Dio();
  static String _baseUrl = "";
  static String _env = "";
  static String _apiKey = "";

  static Dio get dio => _dio;

  static Dio get tokenDio => _tokenDio;

  SHttp._internal() {
    _instance = this;
  }

  static Future<SHttp?> getInstance({
    required String baseUrl,
    String env = "prod",
    String apiKey = "",
    bool showApiLog = true,
    VoidCallback? logout,
    required Future<Response> Function(Dio dio) refresh,
    CacheOptions? cacheOp,
    CachePolicy? policy,
    Future<void> Function(Response res)? setCacheAuthDataAction,
  }) async {
    _instance ?? SHttp._internal();
    _baseUrl = baseUrl;
    _env = env;
    _apiKey = apiKey;
    _showApiLog = showApiLog;

    if (cacheOp != null) {
      cacheOptions = cacheOp;
    } else {
      Directory dir = await getTemporaryDirectory();
      CacheOptions newCacheOp = cacheOptions.copyWith(store: HiveCacheStore(dir.path), policy: policy);
      cacheOptions = newCacheOp;
    }
    await _instance!.init(logout: logout, refresh: refresh);
    return _instance;
  }

  // 初始化Dio
  init({VoidCallback? logout, required Future<Response> Function(Dio dio) refresh}) async {
    final options = BaseOptions(
      baseUrl: _baseUrl,
      connectTimeout: 15.seconds,
      receiveTimeout: 15.seconds,
    );

    /// 初始化dio
    _dio = Dio(options);
    IInterceptors ii = IInterceptors(env: _env, apiKey: _apiKey, showApiLog: _showApiLog);

    _tokenDio = Dio(options);
    _retryDio = Dio(options);

    /// 添加拦截器
    _dio.interceptors.add(ii);
    dio.interceptors.add(cache());
    dio.interceptors.add(auth(tokenDio: _tokenDio, retryDio: _retryDio, logout: logout, refresh: refresh));
    if (addSentry) dio.addSentry();

    await initUserAgent();
    // 添加转换器
    // _dio.transformer = BackgroundTransformer();
  }

  Interceptor cache() {
    return DioCacheInterceptor(options: cacheOptions);
  }

  // 刷新token拦截器
  Interceptor auth({
    required Dio tokenDio,
    required Dio retryDio,
    VoidCallback? logout,
    required Future<Response> Function(Dio dio) refresh,
  }) {
    return AuthInterceptor(
      tokenDio: tokenDio,
      retryDio: retryDio,
      logout: logout ?? () {},
      refresh: refresh,
    );
  }

  // api 请求
  static Future<Response<T>> request<T>(
    String url, {
    String method = "get",
    CachePolicy? cachePolicy,
    Object? data,
    Map<String, dynamic>? queryParameters,
    CancelToken? cancelToken,
    Options? options,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) {
    options = options ??
        Options(
          method: method,
          extra: cacheOptions.copyWith(policy: cachePolicy).toExtra(),
        );

    return dio.request(
      url,
      data: data,
      queryParameters: queryParameters,
      cancelToken: cancelToken,
      options: options,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
  }

  // 验证是否有缓存
  static Future<bool> hasCache(String path, {Map<String, dynamic>? queryParameters}) async {
    RequestOptions options = RequestOptions(
      path: path,
      baseUrl: _baseUrl,
      queryParameters: queryParameters,
    );

    final key = CacheOptions.defaultCacheKeyBuilder(options);

    CacheStore? store = cacheOptions.store;
    bool? exits = await store?.exists(key);
    return exits ?? false;
  }

  // 移除Cache
  static Future<void> deleteCache(String path, {Map<String, dynamic>? queryParameters}) async {
    RequestOptions options = RequestOptions(
      path: path,
      baseUrl: _baseUrl,
      queryParameters: queryParameters,
    );

    final key = CacheOptions.defaultCacheKeyBuilder(options);
    CacheStore? store = cacheOptions.store;
    await store?.delete(key);
    logger.w("Remove cache: ${options.uri}");
  }

  // 获取缓存
  static Future<Response?> getCache(String path, {Map<String, dynamic>? queryParameters}) async {
    RequestOptions options = RequestOptions(
      path: path,
      baseUrl: _baseUrl,
      queryParameters: queryParameters,
    );

    final key = CacheOptions.defaultCacheKeyBuilder(options);

    CacheStore? store = cacheOptions.store;
    if (store == null) return null;
    CacheResponse? res = await store.get(key);

    res?.toResponse(options);
    return res?.toResponse(options);
  }

  static cleanStore() async {
    CacheStore? store = cacheOptions.store;
    await store?.clean();
  }

  // 获取Header
  static Future<Map<String, dynamic>> getHeaders() async {
    String accessTokenStr = SharedPrefs.getString('access_token');
    Map? location = SharedPrefs.getObject('location');

    // User Agent
    String userAgent = SharedPrefs.getString('user_agent');
    Map<String, dynamic> header = {
      'Accept': 'application/json',
      "Accept-Language": Get.locale?.languageCode ?? 'en',
      "Content-Type": "application/json;charset=UTF-8",
      "X-Requested-With": "XMLHttpRequest",
      "App-Env": _env,
      "Api-Key": _apiKey,
      "Authorization": accessTokenStr != "" ? "Bearer $accessTokenStr" : null,
      "User-Agent": userAgent,
      "lat": location?['lat'],
      "lng": location?['lng'],
    };

    if (kIsWeb) header.remove('User-Agent');

    return header;
  }

  static initUserAgent() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    String appVersion = packageInfo.version;
    String buildNumber = packageInfo.buildNumber;

    Map userAgent = {};
    if (!kIsWeb && Platform.isIOS) {
      DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
      IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
      userAgent = {
        "name": iosInfo.utsname.machine,
        "system_name": iosInfo.systemName,
        "platform": Platform.operatingSystem,
        "system_version": iosInfo.systemVersion,
        "model": iosInfo.localizedModel,
        "language": Get.locale.toString(),
        "app_version": appVersion,
        "build_number": buildNumber,
        "app_env": _env,
      };
    } else if (!kIsWeb && Platform.isAndroid) {
      DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
      AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
      userAgent = {
        "name": androidInfo.brand,
        "system_name": androidInfo.device,
        "platform": Platform.operatingSystem,
        "model": androidInfo.model,
        "language": Get.locale.toString(),
        "app_version": appVersion,
        "build_number": buildNumber,
        "app_env": _env,
      };
    } else {
      userAgent = {
        "language": Get.locale.toString(),
        "platform": Platform.operatingSystem,
        "app_version": appVersion,
        "build_number": buildNumber,
        "app_env": _env,
      };
    }
    SharedPrefs.putString("user_agent", jsonEncode(userAgent));
  }

  static UserAgentModel getUserAgent() {
    String v = SharedPrefs.getString("user_agent");

    return UserAgentModel.fromJson(jsonDecode(v != '' ? v : '{}'));
  }

}
